use std::fmt;

use from_pest::FromPest;
use pest::iterators;
use pest_ast::FromPest;
use tracing::debug;
use tracing::trace;

use crate::ast_define::MetaInfo;
use crate::ast_define::Rule;
use crate::ast_define::declear::*;
use crate::ast_define::general::*;
use crate::ast_define::get_meta_info;
use crate::ast_define::keyword::*;
use crate::ast_define::operator::*;

// * 语句 Stmt ->
// *   'if' '(' Cond ')' Stmt ['else' Stmt]
// * | 'while' '(' Cond ')' Stmt
// * | 'break' ';'
// * | 'continue' ';'
// * | 'return' [Exp] ';'
// * | Block
// * | LVal '=' Exp ';'
// * | [Exp] ';'

// Else_Stmt = { ELSE ~ Stmt }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Else_Stmt))]
pub struct Else_Stmt {
    pub else_: ELSE,
    pub stmt: Stmt,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl Else_Stmt {
    /// 带缩进的格式化方法
    pub fn fmt_with_indent(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
        write!(f, "{}", self.else_)?;
        match &self.stmt {
            // `else {\n    stmt\n}\n`
            Stmt::Block(_) => {
                write!(f, " ")?;
                self.stmt.fmt_with_indent(f, indent)?;
                writeln!(f)?; // Block后换行
                Ok(())
            }
            // `else if (cond) ...`
            // 交给 If_Stmt 处理
            Stmt::If_Stmt(_) => {
                write!(f, " ")?;
                self.stmt.fmt_with_indent(f, indent)
            }
            // `else\n    stmt;\n`
            _ => {
                writeln!(f)?;
                write!(f, "{}", "    ".repeat(indent + 1))?;
                self.stmt.fmt_with_indent(f, indent + 1)
                // 单行语句本身已经有换行，不需要额外换行
            }
        }
    }
}

impl fmt::Display for Else_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.fmt_with_indent(f, 0)
    }
}

// If_Stmt = { IF ~ "(" ~ Cond ~ ")" ~ Stmt ~ Else_Stmt? }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::If_Stmt))]
pub struct If_Stmt {
    pub if_: IF,
    pub cond: Cond,
    pub stmt: Box<Stmt>,
    pub else_stmt: Option<Box<Else_Stmt>>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl If_Stmt {
    /// 带缩进的格式化方法
    pub fn fmt_with_indent(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
        let indent_str = "    ".repeat(indent);
        write!(f, "{} ({})", self.if_, self.cond)?;

        if matches!(self.stmt.as_ref(), Stmt::Block(_)) {
            // Block情况：if (cond) { ... }
            write!(f, " ")?;
            self.stmt.fmt_with_indent(f, indent)?;
            writeln!(f)?; // Block后换行
        } else {
            // 单行语句情况：if (cond)\n    stmt;
            writeln!(f)?;
            write!(f, "{}", "    ".repeat(indent + 1))?;
            self.stmt.fmt_with_indent(f, indent + 1)?;
            // 单行语句本身已经有换行，不需要额外换行
        }

        if let Some(else_stmt) = self.else_stmt.as_ref() {
            write!(f, "{indent_str}")?; // else的缩进与if相同
            else_stmt.fmt_with_indent(f, indent)?;
        }
        Ok(())
    }
}

impl fmt::Display for If_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.fmt_with_indent(f, 0)
    }
}

// While_Stmt = { WHILE ~ "(" ~ Cond ~ ")" ~ Stmt }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::While_Stmt))]
pub struct While_Stmt {
    pub while_: WHILE,
    pub cond: Cond,
    pub stmt: Box<Stmt>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl While_Stmt {
    /// 带缩进的格式化方法
    pub fn fmt_with_indent(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
        write!(f, "{} ({})", self.while_, self.cond)?;

        if matches!(self.stmt.as_ref(), Stmt::Block(_)) {
            // `while (cond) { ... }`
            write!(f, " ")?;
            self.stmt.fmt_with_indent(f, indent)?;
            writeln!(f) // Block后换行
        } else {
            // `while (cond)\n    stmt;`
            writeln!(f)?;
            write!(f, "{}", "    ".repeat(indent + 1))?;
            self.stmt.fmt_with_indent(f, indent + 1)
            // 单行语句本身已经有换行，不需要额外换行
        }
    }
}

impl fmt::Display for While_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.fmt_with_indent(f, 0)
    }
}

// Break_Stmt = { BREAK ~ ";" }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Break_Stmt))]
pub struct Break_Stmt {
    pub break_: BREAK,
}

impl fmt::Display for Break_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{};", self.break_)
    }
}

// Continue_Stmt = { CONTINUE ~ ";" }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Continue_Stmt))]
pub struct Continue_Stmt {
    pub continue_: CONTINUE,
}

impl fmt::Display for Continue_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{};", self.continue_)
    }
}

// Return_Stmt = { RETURN ~ Exp? ~ ";" }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Return_Stmt))]
pub struct Return_Stmt {
    pub return_: RETURN,
    pub exp: Option<Exp>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for Return_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.return_)?;
        if let Some(exp) = self.exp.as_ref() {
            write!(f, " ")?;
            write!(f, "{exp}")?;
        }
        writeln!(f, ";")
    }
}

// LVal_Assign_Exp_Stmt = { LVal ~ ASSIGN ~ Exp ~ ";" }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::LVal_Assign_Exp_Stmt))]
pub struct LVal_Assign_Exp_Stmt {
    pub l_val: LVal,
    pub assign: ASSIGN,
    pub exp: Exp,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for LVal_Assign_Exp_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{} {} {};", self.l_val, self.assign, self.exp)
    }
}

// Exp_Stmt = { Exp? ~ ";" }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Exp_Stmt))]
pub struct Exp_Stmt {
    pub exp: Option<Exp>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for Exp_Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(exp) = self.exp.as_ref() {
            write!(f, "{exp}")?;
        }
        writeln!(f, ";")
    }
}

// Stmt = {
//     If_Stmt
//   | While_Stmt
//   | Break_Stmt
//   | Continue_Stmt
//   | Return_Stmt
//   | Block
//   | LVal_Assign_Exp_Stmt
//   | Exp_Stmt
// }
#[derive(Debug, PartialEq)]
pub enum Stmt {
    If_Stmt(If_Stmt),
    While_Stmt(While_Stmt),
    Break_Stmt(Break_Stmt),
    Continue_Stmt(Continue_Stmt),
    Block(Block),
    Return_Stmt(Return_Stmt),
    LVal_Assign_Exp_Stmt(LVal_Assign_Exp_Stmt),
    Exp_Stmt(Exp_Stmt),
}

impl Stmt {
    /// 带缩进的格式化方法
    pub fn fmt_with_indent(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
        match self {
            Stmt::Block(block) => block.fmt_with_indent(f, indent),
            Stmt::If_Stmt(if_stmt) => if_stmt.fmt_with_indent(f, indent),
            Stmt::While_Stmt(while_stmt) => while_stmt.fmt_with_indent(f, indent),

            Stmt::Exp_Stmt(this) => write!(f, "{this}"),
            Stmt::Break_Stmt(this) => write!(f, "{this}"),
            Stmt::Return_Stmt(this) => write!(f, "{this}"),
            Stmt::Continue_Stmt(this) => write!(f, "{this}"),
            Stmt::LVal_Assign_Exp_Stmt(this) => write!(f, "{this}"),
        }
    }
}

impl fmt::Display for Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.fmt_with_indent(f, 0)
    }
}

impl<'pest> FromPest<'pest> for Stmt {
    type Rule = Rule;
    type FatalError = from_pest::Void;
    #[tracing::instrument(skip_all)]
    fn from_pest(
        pest: &mut iterators::Pairs<'pest, Rule>,
    ) -> Result<Self, from_pest::ConversionError<from_pest::Void>> {
        trace!("正在解析 Stmt");
        let pair = pest.next().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("pair in Stmt:\n{pair:#?}");
        let Rule::Stmt = pair.as_rule() else {
            trace!("\n{:#?} != Rule::Stmt", pair.as_rule());
            return Err(from_pest::ConversionError::NoMatch);
        };

        let mut inner = pair.into_inner();
        trace!("inner:\n{inner:#?}");
        let inner_pair = inner.peek().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("inner_pair:\n{inner_pair:#?}");
        match inner_pair.as_rule() {
            Rule::If_Stmt => Ok(Stmt::If_Stmt(If_Stmt::from_pest(&mut inner)?)),
            Rule::While_Stmt => Ok(Stmt::While_Stmt(While_Stmt::from_pest(&mut inner)?)),
            Rule::Break_Stmt => Ok(Stmt::Break_Stmt(Break_Stmt::from_pest(&mut inner)?)),
            Rule::Continue_Stmt => Ok(Stmt::Continue_Stmt(Continue_Stmt::from_pest(&mut inner)?)),
            Rule::Block => Ok(Stmt::Block(Block::from_pest(&mut inner)?)),
            Rule::Return_Stmt => Ok(Stmt::Return_Stmt(Return_Stmt::from_pest(&mut inner)?)),
            Rule::LVal_Assign_Exp_Stmt => Ok(Stmt::LVal_Assign_Exp_Stmt(
                LVal_Assign_Exp_Stmt::from_pest(&mut inner)?,
            )),
            Rule::Exp_Stmt => Ok(Stmt::Exp_Stmt(Exp_Stmt::from_pest(&mut inner)?)),
            _ => Err(from_pest::ConversionError::NoMatch),
        }
    }
}

// *语句块项 BlockItem -> Decl | Stmt
#[derive(Debug, PartialEq)]
pub enum BlockItem {
    Decl(Decl),
    Stmt(Stmt),
}

impl BlockItem {
    /// 带缩进的格式化方法
    pub fn fmt_with_indent(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
        match self {
            // * BlockItem::Decl 仅出现在函数体内, 因此需要换行
            BlockItem::Decl(decl) => writeln!(f, "{decl}"),
            BlockItem::Stmt(stmt) => {
                stmt.fmt_with_indent(f, indent)?;
                // 只有Block语句需要额外换行，if/while语句根据内部结构自己处理换行
                matches!(stmt, Stmt::Block(_)).then(|| writeln!(f));
                Ok(())
            }
        }
    }
}

impl fmt::Display for BlockItem {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.fmt_with_indent(f, 0)
    }
}

impl<'pest> FromPest<'pest> for BlockItem {
    type Rule = Rule;
    type FatalError = from_pest::Void;
    #[tracing::instrument(skip_all)]
    fn from_pest(
        pest: &mut iterators::Pairs<'pest, Rule>,
    ) -> Result<Self, from_pest::ConversionError<from_pest::Void>> {
        trace!("正在解析 BlockItem");
        let pair = pest.next().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("pair in BlockItem:\n{pair:#?}");
        let Rule::BlockItem = pair.as_rule() else {
            trace!("\n{:#?} != Rule::BlockItem", pair.as_rule());
            return Err(from_pest::ConversionError::NoMatch);
        };

        let mut inner = pair.into_inner();
        trace!("inner:\n{inner:#?}");
        let inner_pair = inner.peek().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("inner_pair:\n{inner_pair:#?}");
        match inner_pair.as_rule() {
            Rule::Decl => Ok(BlockItem::Decl(Decl::from_pest(&mut inner)?)),
            Rule::Stmt => Ok(BlockItem::Stmt(Stmt::from_pest(&mut inner)?)),
            _ => Err(from_pest::ConversionError::NoMatch),
        }
    }
}

// * 语句块 Block = { "{" ~ (BlockItem)* ~ "}" }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Block))]
pub struct Block {
    pub block_item: Vec<BlockItem>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl Block {
    /// 带缩进的格式化方法，用于语句块的正确格式化
    pub fn fmt_with_indent(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
        let outer_indent = "    ".repeat(indent);
        let inner_indent = "    ".repeat(indent + 1);

        writeln!(f, "{{")?; // 开大括号后总是换行
        for block_item in &self.block_item {
            write!(f, "{inner_indent}")?;
            block_item.fmt_with_indent(f, indent + 1)?;
            // 不在这里强制换行，让每个语句自己控制换行
        }
        write!(f, "{outer_indent}}}") // 闭大括号前加缩进，不换行（让调用者决定）
    }
}

impl fmt::Display for Block {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.fmt_with_indent(f, 0)
    }
}
